

    /*global XDomainRequest */

    sgvizler.query = function (container) {
        this.container = container;

        //defaults
        this.query = "SELECT ?class (count(?instance) AS ?noOfInstances)\nWHERE{ ?instance a ?class }\nGROUP BY ?class\nORDER BY ?class";
        this.endpoint = "http://sws.ifi.uio.no/sparql/world";
        this.endpoint_output = 'json';  // xml, json, jsonp
        this.endpoint_query_url = "?output=text&amp;query=";
        this.validator_query_url = "http://www.sparql.org/query-validator?languageSyntax=SPARQL&amp;outputFormat=sparql&amp;linenumbers=true&amp;query=";
        this.chart = 'gLineChart';
        this.loglevel = 2;
	//this.preprocess; // name of function to preprocess dataTable.

        this.chartOptions = {
            'width':           '800',
            'height':          '400',
            'chartArea':       { left: '5%', top: '5%', width: '75%', height: '80%' },
            'gGeoMap': {
                'dataMode':           'markers'
            },
            'gMap': {
                'dataMode':           'markers'
            },
            'sMap': {
                'dataMode':           'markers',
                'showTip':            true,
                'useMapTypeControl':  true
            },
            'gSparkline': {
                'showAxisLines':      false
            }
        };
    };

    sgvizler.query.prototype.draw = function (queryOpt, chartOpt) {
        var that = this,
            chartFunc;

        if (sgvizler.ui.isElement(this.container)) {
            $.extend(this,                                            // default values are overwritten by
                     sgvizler.option.query,                           // values set globally by user are overwritten by
                     sgvizler.ui.getQueryOptionAttr(this.container),  // values set in html container are overwritten by
                     queryOpt);                                       // values in function parameter.

            $.extend(this.chartOptions,                               // default values are overwritten by ...
                     this.chartOptions[this.chart],                   // default chart specific values are overwritten by ...
                     sgvizler.option.chart,                           // values set globally by user are overwritten by ...
                     sgvizler.ui.getChartOptionAttr(this.container),  // values set in <div> are overwritten by ...
                     chartOpt);                                       // function parameter.
            chartFunc = sgvizler.charts.getChart(this.container, this.chart);
            this.insertFrom();
            this.runQuery(function (data) {
                var dataTable = new google.visualization.DataTable(that.processQueryResults(data));
                if (that.preprocess) {
                    dataTable = global[that.preprocess](dataTable);
                }
                chartFunc.draw(dataTable, that.chartOptions);
            });
        }
    };

    sgvizler.query.prototype.runQuery = function (drawFunc) {
        var that = this,
            xdr,
            url;
        sgvizler.ui.loadingChart();
        sgvizler.ui.displayFeedback(this, "LOADING");
        this.encodedQuery = encodeURIComponent(this.getPrefixes() + this.query);
        if (this.endpoint_output !== 'jsonp' && $.browser.msie && window.XDomainRequest) {
            xdr = new XDomainRequest();
            url = this.endpoint +
                "?query=" + this.encodedQuery +
                "&output=" + this.endpoint_output;
            xdr.open("GET", url);
            xdr.onload = function () {
                var data;
                if (that.endpoint_output === "xml") {
                    data = $.parseXML(xdr.responseText);
                } else {
                    data = $.parseJSON(xdr.responseText);
                }
                drawFunc(data);
                sgvizler.ui.doneLoadingChart();
            };
            xdr.send();
        } else {
            $.ajax({
                url: this.endpoint,
                data:  { query: this.getPrefixes() + this.query,
                         output: (this.endpoint_output === 'jsonp') ? 'json' : this.endpoint_output },
                dataType: this.endpoint_output,
                // statusCode: {
                //     403: function() {
                //         console.log("forbidden");
                //     }
                // },
                success: function (data) { //, textStatus, xhr) {
                    drawFunc(data);
                    //console.log("Success: " + xhr.status + ", " + that.container);
                },
                error: function () { //xhr, textStatus, errorThrown) {
                    sgvizler.ui.displayFeedback(that, "ERROR_ENDPOINT");
                    //console.log("Error: " + xhr.status + ", " + that.container);
                },
                complete: function () { //xhr, textStatus) {
                    sgvizler.ui.doneLoadingChart();
                    //console.log("Complete: " + xhr.status + ", " + that.container);
                }
            });
        }
    };

    sgvizler.query.prototype.processQueryResults = function (data) {
        this.setResultRowCount(data);
        if (this.noRows === null) {
            sgvizler.ui.displayFeedback(this, "ERROR_UNKNOWN");
        } else if (this.noRows === 0) {
            sgvizler.ui.displayFeedback(this, "NO_RESULTS");
        } else {
            sgvizler.ui.displayFeedback(this, "DRAWING");
            return this.getGoogleJSON(data);
        }
    };

    sgvizler.query.prototype.setResultRowCount = function (data) {
        if (this.endpoint_output === 'xml') {
            this.noRows = sgvizler.parser.countRowsSparqlXML(data);
        } else {
            this.noRows = sgvizler.parser.countRowsSparqlJSON(data);
        }
    };

    sgvizler.query.prototype.getGoogleJSON = function (data) {
        if (this.endpoint_output === 'xml') {
            data = sgvizler.parser.convertSparqlXML2GoogleJSON(data);
        } else {
            data = sgvizler.parser.convertSparqlJSON2GoogleJSON(data);
        }
        return data;
    };

    sgvizler.query.prototype.insertFrom = function () {
        if (this.rdf !== undefined) {
            var i,
                froms = this.rdf.split(sgvizler.ui.attr.valueSplit),
                from = "";
            for (i = 0; i < froms.length; i += 1) {
                from += 'FROM <' + froms[i] + '>\n';
            }
            this.query = this.query.replace(/(WHERE)?(\s)*\{/, '\n' + from + 'WHERE {');
        }
    };

    sgvizler.query.prototype.getPrefixes = function () {
        var prefix,
            prefixes = "";
        for (prefix in sgvizler.option.namespace) {
            if (sgvizler.option.namespace.hasOwnProperty(prefix)) {
                prefixes += "PREFIX " + prefix + ": <" + sgvizler.option.namespace[prefix] + ">\n";
            }
        }
        return prefixes;
    };

